package com.tonyodev.fetch2.fetch;

import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import com.tonyodev.fetch2.Download;
import com.tonyodev.fetch2.FetchConfiguration;
import com.tonyodev.fetch2.database.DownloadDatabase;
import com.tonyodev.fetch2.database.DownloadInfo;
import com.tonyodev.fetch2.database.FetchDatabaseManager;
import com.tonyodev.fetch2.database.FetchDatabaseManagerImpl;
import com.tonyodev.fetch2.database.FetchDatabaseManagerWrapper;
import com.tonyodev.fetch2.downloader.DownloadManager;
import com.tonyodev.fetch2.downloader.DownloadManagerCoordinator;
import com.tonyodev.fetch2.downloader.DownloadManagerImpl;
import com.tonyodev.fetch2.helper.DownloadInfoUpdater;
import com.tonyodev.fetch2.helper.PriorityListProcessor;
import com.tonyodev.fetch2.helper.PriorityListProcessorImpl;
import com.tonyodev.fetch2.provider.DownloadProvider;
import com.tonyodev.fetch2.provider.GroupInfoProvider;
import com.tonyodev.fetch2.provider.NetworkInfoProvider;
import com.tonyodev.fetch2.util.FetchUtils;
import com.tonyodev.fetch2core.DefaultStorageResolver;
import com.tonyodev.fetch2core.FetchCoreUtils;
import com.tonyodev.fetch2core.HandlerWrapper;

import java.io.IOException;
import java.util.LinkedHashMap;

/**
 * @since 2021-05-20
 */
public class FetchModulesBuilder {

    private final static Object lock = new Object();
    private final EventHandler mainUIHandler = new EventHandler(EventRunner.getMainEventRunner());
    private final static LinkedHashMap<String, Holder> holderMap = new LinkedHashMap<>();

    private FetchModulesBuilder() {
    }

    static class HolderInstance {
        static final FetchModulesBuilder HOLDER = new FetchModulesBuilder();
    }

    public static FetchModulesBuilder getInstance() {
        return HolderInstance.HOLDER;
    }

    public EventHandler getMainUIHandler() {
        return mainUIHandler;
    }


    public Modules buildModulesFromPrefs(FetchConfiguration fetchConfiguration) {
        synchronized (lock) {
            Holder holder = holderMap.get(fetchConfiguration.getNamespace());
            Modules modules ;
            if (holder != null) {
                modules = new Modules(fetchConfiguration, holder.handlerWrapper, holder.fetchDatabaseManagerWrapper, holder.downloadProvider,
                    holder.groupInfoProvider, holder.uiHandler, holder.downloadManagerCoordinator, holder.listenerCoordinator);
            } else {
                HandlerWrapper newHandlerWrapper = new HandlerWrapper(fetchConfiguration.getNamespace(), fetchConfiguration.getBackgroundHandler());
                LiveSettings liveSettings = new LiveSettings(fetchConfiguration.getNamespace());
                FetchDatabaseManager newDatabaseManager = null;
                if (fetchConfiguration.getFetchNotificationManager() != null) {
                    newDatabaseManager = fetchConfiguration.getFetchDatabaseManager();

                } else {
                    newDatabaseManager = new FetchDatabaseManagerImpl(
                        fetchConfiguration.getAppContext(),
                        fetchConfiguration.getNamespace(),
                        fetchConfiguration.getLogger(),
                        DownloadDatabase.getMigrations(),
                        liveSettings,
                        fetchConfiguration.getFileExistChecksEnabled(),
                        new DefaultStorageResolver(fetchConfiguration.getAppContext(),
                            FetchCoreUtils.getFileTempDir(fetchConfiguration.getAppContext())));
                }
                FetchDatabaseManagerWrapper databaseManagerWrapper = new FetchDatabaseManagerWrapper(newDatabaseManager);
                DownloadProvider downloadProvider = new DownloadProvider(databaseManagerWrapper);
                DownloadManagerCoordinator downloadManagerCoordinator = new DownloadManagerCoordinator(fetchConfiguration.getNamespace());

                GroupInfoProvider groupInfoProvider = new GroupInfoProvider(fetchConfiguration.getNamespace(), downloadProvider);
                ListenerCoordinator listenerCoordinator = new ListenerCoordinator(fetchConfiguration.getNamespace(), groupInfoProvider, downloadProvider, mainUIHandler);

                Modules newModules = new Modules(fetchConfiguration, newHandlerWrapper, databaseManagerWrapper, downloadProvider, groupInfoProvider, mainUIHandler,
                    downloadManagerCoordinator, listenerCoordinator);
                holderMap.put(fetchConfiguration.getNamespace(), new Holder(newHandlerWrapper, databaseManagerWrapper, downloadProvider, groupInfoProvider, mainUIHandler,
                    downloadManagerCoordinator, listenerCoordinator, newModules.networkInfoProvider));
                return newModules;

            }
            modules.handlerWrapper.incrementUsageCounter();
            return modules;

        }

    }

    public static void removeNamespaceInstanceReference(String namespace) throws IOException {
        synchronized (lock) {
            //  报错隐藏
            Holder holder = holderMap.get(namespace);
            if (holder != null) {
                holder.handlerWrapper.decrementUsageCounter();
                if (holder.handlerWrapper.usageCount() == 0) {
                    holder.handlerWrapper.close();
                    holder.listenerCoordinator.clearAll();
                    holder.groupInfoProvider.clear();
                    holder.fetchDatabaseManagerWrapper.close();
                    holder.downloadManagerCoordinator.clearAll();
                    holder.networkInfoProvider.unregisterAllNetworkChangeListeners();
                    holderMap.remove(namespace);
                }
            }
        }
    }


    public static class Holder {
        private HandlerWrapper handlerWrapper;
        private FetchDatabaseManagerWrapper fetchDatabaseManagerWrapper;
        private DownloadProvider downloadProvider;
        private GroupInfoProvider groupInfoProvider;
        private EventHandler uiHandler;
        private DownloadManagerCoordinator downloadManagerCoordinator;
        private ListenerCoordinator listenerCoordinator;
        private NetworkInfoProvider networkInfoProvider;

        public HandlerWrapper getHandlerWrapper() {
            return handlerWrapper;
        }

        public FetchDatabaseManagerWrapper getFetchDatabaseManagerWrapper() {
            return fetchDatabaseManagerWrapper;
        }

        public DownloadProvider getDownloadProvider() {
            return downloadProvider;
        }

        public GroupInfoProvider getGroupInfoProvider() {
            return groupInfoProvider;
        }

        public EventHandler getUiHandler() {
            return uiHandler;
        }

        public DownloadManagerCoordinator getDownloadManagerCoordinator() {
            return downloadManagerCoordinator;
        }

        public ListenerCoordinator getListenerCoordinator() {
            return listenerCoordinator;
        }

        public NetworkInfoProvider getNetworkInfoProvider() {
            return networkInfoProvider;
        }

        public Holder(HandlerWrapper handlerWrapper,
                      FetchDatabaseManagerWrapper fetchDatabaseManagerWrapper,
                      DownloadProvider downloadProvider,
                      GroupInfoProvider groupInfoProvider,
                      EventHandler uiHandler,
                      DownloadManagerCoordinator downloadManagerCoordinator,
                      ListenerCoordinator listenerCoordinator,
                      NetworkInfoProvider networkInfoProvider) {
            this.handlerWrapper = handlerWrapper;
            this.fetchDatabaseManagerWrapper = fetchDatabaseManagerWrapper;
            this.downloadProvider = downloadProvider;
            this.groupInfoProvider = groupInfoProvider;
            this.uiHandler = uiHandler;
            this.downloadManagerCoordinator = downloadManagerCoordinator;
            this.listenerCoordinator = listenerCoordinator;
            this.networkInfoProvider = networkInfoProvider;
        }
    }

    public static class Modules {
        private FetchConfiguration fetchConfiguration;
        private HandlerWrapper handlerWrapper;
        private FetchDatabaseManagerWrapper fetchDatabaseManagerWrapper;
        private DownloadProvider downloadProvider;
        private GroupInfoProvider groupInfoProvider;
        private EventHandler uiHandler;
        private DownloadManagerCoordinator downloadManagerCoordinator;
        private ListenerCoordinator listenerCoordinator;

        public FetchConfiguration getFetchConfiguration() {
            return fetchConfiguration;
        }

        public HandlerWrapper getHandlerWrapper() {
            return handlerWrapper;
        }

        public FetchDatabaseManagerWrapper getFetchDatabaseManagerWrapper() {
            return fetchDatabaseManagerWrapper;
        }

        public DownloadProvider getDownloadProvider() {
            return downloadProvider;
        }

        public GroupInfoProvider getGroupInfoProvider() {
            return groupInfoProvider;
        }

        public EventHandler getUiHandler() {
            return uiHandler;
        }

        public ListenerCoordinator getListenerCoordinator() {
            return listenerCoordinator;
        }

        public DownloadManagerCoordinator getDownloadManagerCoordinator() {
            return downloadManagerCoordinator;
        }


        DownloadManager downloadManager;
        PriorityListProcessor<Download> priorityListProcessor;
        DownloadInfoUpdater downloadInfoUpdater;
        NetworkInfoProvider networkInfoProvider;
        FetchHandler fetchHandler;

        public DownloadManager getDownloadManager() {
            return downloadManager;
        }

        public PriorityListProcessor<Download> getPriorityListProcessor() {
            return priorityListProcessor;
        }

        public DownloadInfoUpdater getDownloadInfoUpdater() {
            return downloadInfoUpdater;
        }

        public NetworkInfoProvider getNetworkInfoProvider() {
            return networkInfoProvider;
        }

        public FetchHandler getFetchHandler() {
            return fetchHandler;
        }

        public Modules(FetchConfiguration fetchConfiguration,
                       HandlerWrapper handlerWrapper,
                       FetchDatabaseManagerWrapper fetchDatabaseManagerWrapper,
                       DownloadProvider downloadProvider,
                       GroupInfoProvider groupInfoProvider,
                       EventHandler uiHandler,
                       DownloadManagerCoordinator downloadManagerCoordinator,
                       ListenerCoordinator listenerCoordinator) {
            this.fetchConfiguration = fetchConfiguration;
            this.handlerWrapper = handlerWrapper;
            this.fetchDatabaseManagerWrapper = fetchDatabaseManagerWrapper;
            this.downloadProvider = downloadProvider;
            this.groupInfoProvider = groupInfoProvider;
            this.uiHandler = uiHandler;
            this.downloadManagerCoordinator = downloadManagerCoordinator;
            this.listenerCoordinator = listenerCoordinator;

            init();

        }

        private void init() {
            downloadInfoUpdater = new DownloadInfoUpdater(fetchDatabaseManagerWrapper);
            networkInfoProvider = new NetworkInfoProvider(fetchConfiguration.getAppContext(), fetchConfiguration.getInternetCheckUrl());

            downloadManager = new DownloadManagerImpl(
                fetchConfiguration.getHttpDownloader(),
                fetchConfiguration.getConcurrentLimit(),
                fetchConfiguration.getProgressReportingIntervalMillis(),
                fetchConfiguration.getLogger(),
                networkInfoProvider,
                fetchConfiguration.getRetryOnNetworkGain(),
                downloadInfoUpdater,
                downloadManagerCoordinator,
                listenerCoordinator,
                fetchConfiguration.getFileServerDownloader(),
                fetchConfiguration.getHashCheckingEnabled(),
                fetchConfiguration.getStorageResolver(),
                fetchConfiguration.getAppContext(),
                fetchConfiguration.getNamespace(),
                groupInfoProvider,
                fetchConfiguration.getMaxAutoRetryAttempts(),
                fetchConfiguration.getPreAllocateFileOnCreation());

            priorityListProcessor = new PriorityListProcessorImpl(
                handlerWrapper,
                downloadProvider,
                downloadManager,
                networkInfoProvider,
                fetchConfiguration.getLogger(),
                listenerCoordinator,
                fetchConfiguration.getConcurrentLimit(),
                fetchConfiguration.getAppContext(),
                fetchConfiguration.getNamespace(),
                fetchConfiguration.getPrioritySort());
            priorityListProcessor.setGlobalNetworkType(fetchConfiguration.getGlobalNetworkType());

            fetchHandler = new FetchHandlerImpl(
                fetchConfiguration.getNamespace(),
                fetchDatabaseManagerWrapper,
                downloadManager,
                priorityListProcessor,
                fetchConfiguration.getLogger(),
                fetchConfiguration.getAutoStart(),
                fetchConfiguration.getHttpDownloader(),
                fetchConfiguration.getFileServerDownloader(),
                listenerCoordinator,
                uiHandler,
                fetchConfiguration.getStorageResolver(),
                fetchConfiguration.getFetchNotificationManager(),
                groupInfoProvider,
                fetchConfiguration.getPrioritySort(),
                fetchConfiguration.getCreateFileOnEnqueue());

            fetchDatabaseManagerWrapper.setDelegate(new FetchDatabaseManager.Delegate() {
                @Override
                public void deleteTempFilesForDownload(DownloadInfo downloadInfo) {

                    String tempDir = fetchConfiguration.getStorageResolver()
                        .getDirectoryForFileDownloaderTypeParallel(FetchUtils.getRequestForDownload(downloadInfo));
                    FetchUtils.deleteAllInFolderForId(downloadInfo.getId(), tempDir);
                }
            });

        }
    }

}
